// 工具函数库(https://www.html.cn/doc/lodash/)x
import {
    cloneDeep as cloneDeepFunc,
    clone as cloneFunc,
    isEmpty as isEmptyFunc,
    isNaN as isNaNFunc,
    isNumber as isNumberFunc,
    isUndefined as isUndefinedFunc,
    isArray as isArrayFunc,
    throttle as throttleFunc
} from 'lodash';

// 时间库(https://github.com/iamkun/dayjs)
import dayjs from 'dayjs';
import storage from './storage';
import store from '@/store';
import request from '@/request';
import constants from '@/commons/constants';
import { computed } from 'vue';

export const isNaN = isNaNFunc;
export const isNumber = isNumberFunc;
export const isUndefined = isUndefinedFunc;
export const isNotUndefined = args => !isUndefinedFunc(args);
export const isArray = isArrayFunc;
export const clone = cloneFunc;
export const cloneDeep = cloneDeepFunc;
export const isObject = obj => Object.prototype.toString.call(obj) === '[object Object]';

/**
 * @description: 将对象重新封装成{value:'',label:''}型对象,value 原对象属性值为label
 * @param {常量的名称} obj
 * @param {value值是否是number类型} isNumKey
 * @return: Array
 */
export const packSelectObjUtil = function (obj, isNumKey = true) {
    var objArr = [];
    for (var p in obj) {
        var object = {};
        object.value = isNumKey ? Number(p) : p;
        object.label = obj[p];
        objArr.push(object);
    }
    return objArr;
};

/**
 * 检查val是否为空
 * @param {*} val
 * @returns
 */
export const isEmpty = val => {
    if (isNumber(val)) {
        return false;
    }
    return isEmptyFunc(val);
};

/**
 * 检查val是否不为空，0被认为不为空，undefined/null/NaN/{}/[]都会被认为空
 * @param {*} val
 */
export const isNotEmpty = val => {
    if (isNaN(val)) {
        return false;
    }
    if (isNumber(val)) {
        return true;
    }
    return !isEmpty(val);
};

/**
 * 除去对象中空元素
 * @param {*} obj
 * @returns
 */
export const dealObjectEmptyValue = obj => {
    const param = {};
    if (obj === null || obj === undefined || obj === '') return param;
    for (const key in obj) {
        if (obj[key] !== null && obj[key] !== undefined && obj[key] !== '') {
            param[key] = obj[key];
        }
    }
    return param;
};

/**
 * 将val值按照format进行格式化
 * @param {*} val
 * @param {*} format
 */
export const format = (val, format = 'YYYY-MM-DD HH:mm') => {
    return dayjs(val).format(format);
};

/**
 * 传入天数是否早于指定天数，指定天数默认当天
 * @param {*} date
 * @param {*} currentDate
 * @returns
 */
export const isDayBefore = (date, currentDate = format(dayjs(), 'YYYY-MM-DD')) => {
    return dayjs(date).isBefore(currentDate);
};

/**
 * 相差时间，默认天
 * @param {*} date1
 * @param {*} date2
 * @param {*} type
 * @param {*} isFloat
 * @returns
 */
export const dateDiff = (date1, date2, type = 'day', isFloat = false) => {
    return dayjs(date1).diff(date2, type, isFloat);
};

/**
 * 跳转到统一登录入口
 */
export const logoutUtil = () => {
    const env = import.meta.env.MODE;
    if (env === 'development') {
        location.href = import.meta.env.VITE_LOGIN_PATH + '/login?backUrl=' + location.origin;
    } else {
        location.href = window.apollo.VUE_APP_LOGIN_PATH + '/login?backUrl=' + location.origin;
    }
};

/**
 * 格式化时间
 * @param {时间} time
 * @param {类型} type
 * @param {链接符} join
 */
export const dateFormatFilter = function (time) {
    if (!time) return '';
    const date = new Date(time);
    const y = date.getFullYear();
    let m = date.getMonth() + 1;
    let d = date.getDate();
    let h = date.getHours();
    let min = date.getMinutes();
    let s = date.getSeconds();
    m = m < 10 ? '0' + m : m;
    d = d < 10 ? '0' + d : d;
    h = h < 10 ? '0' + h : h;
    min = min < 10 ? '0' + min : min;
    s = s < 10 ? '0' + s : s;

    return `${y}-${m}-${d} ${h}:${min}`;
};

/**
 * 是否为手机号
 * @param {*} mobile
 * @returns
 */
export const isMobile = mobile => {
    const phone_pattern = new RegExp('^1[3-9][0-9]{9}$', 'i');
    return phone_pattern.test(mobile);
};

/**
 * 是否是Url链接
 * @param {*} link
 * @returns
 */
export const isLink = link => {
    const reg =
        /(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
    return reg.test(link);
};

/**
 * 密码规则: 至少8-16个字符，由数字或字母组成，不能包含特殊字符。
 * @param {*} password
 * @returns
 */
export const isPassword = password => {
    return /^[A-Za-z0-9\d]{8,16}$/.test(password);
};

/**
 * 大于等于零整数。(不能有0123这样的值)
 * @param {*} number
 * @returns
 */
export const isPositiveInt = number => {
    let flag = typeof number == 'string' && number.length > 1 && number.substr(0, 1) === '0';
    if (flag) {
        return false;
    } else {
        return /^[0-9]+[0-9]*$/.test(number);
    }
};

/**
 * 货币金额(支持正数,保留两位小数)
 * @param {*} number
 * @returns
 */
export const isAmount = number => {
    return /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0){1}$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/.test(number);
};

/**
 * 将科学计数法的数字转为字符串
 * 说明：运算精度丢失方法中处理数字的时候，如果出现科学计数法，就会导致结果出错
 * @param  num
 */
export const toNonExponential = num => {
    if (num == null) {
        return num;
    }
    if (typeof num == 'number') {
        var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
        return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
    } else {
        return num;
    }
};
/**
 * 乘法 - js运算精度丢失问题
 * @param arg1  数1
 * @param arg2  数2
 */
export const floatMultiply = (arg1, arg2 = 100) => {
    arg1 = Number(arg1);
    arg2 = Number(arg2);
    if ((!arg1 && arg1 !== 0) || (!arg2 && arg2 !== 0)) {
        return null;
    }
    arg1 = toNonExponential(arg1);
    arg2 = toNonExponential(arg2);
    var n1, n2;
    var r1, r2; // 小数位数
    try {
        r1 = arg1.toString().split('.')[1].length;
    } catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split('.')[1].length;
    } catch (e) {
        r2 = 0;
    }
    n1 = Number(arg1.toString().replace('.', ''));
    n2 = Number(arg2.toString().replace('.', ''));
    return (n1 * n2) / Math.pow(10, r1 + r2);
};

/**
 * 除法 - js运算精度丢失问题
 * @param arg1  数1
 * @param arg2  数2
 */
export const floatDivide = (arg1, arg2) => {
    arg1 = Number(arg1);
    arg2 = Number(arg2);
    if (!arg2) {
        return null;
    }
    if (!arg1 && arg1 !== 0) {
        return null;
    } else if (arg1 === 0) {
        return 0;
    }
    arg1 = toNonExponential(arg1);
    arg2 = toNonExponential(arg2);
    var n1, n2;
    var r1, r2; // 小数位数
    try {
        r1 = arg1.toString().split('.')[1].length;
    } catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split('.')[1].length;
    } catch (e) {
        r2 = 0;
    }
    n1 = Number(arg1.toString().replace('.', ''));
    n2 = Number(arg2.toString().replace('.', ''));
    return floatMultiply(n1 / n2, Math.pow(10, r2 - r1));
    // return (n1 / n2) * Math.pow(10, r2 - r1);   // 直接乘法还是会出现精度问题
};

/**
 * form正则校验
 * @params type
 * number(纯数字)
 * chinese(纯中文)
 * phone(手机号)
 * idcard(一代/二代身份证)
 * email(邮箱)
 * bankcard(银行卡)
 * money(金额)
 * creditCode(社会信用代码)
 * code(组织机构代码)
 * business(营业执照15位)
 */
export const getRegExp = type => {
    const formReg = {
        number: /^\d+$/,
        chinese:
            /^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/,
        phone: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/,
        idcard: /^\d{6}((((((19|20)\d{2})(0[13-9]|1[012])(0[1-9]|[12]\d|30))|(((19|20)\d{2})(0[13578]|1[02])31)|((19|20)\d{2})02(0[1-9]|1\d|2[0-8])|((((19|20)([13579][26]|[2468][048]|0[48]))|(2000))0229))\d{3})|((((\d{2})(0[13-9]|1[012])(0[1-9]|[12]\d|30))|((\d{2})(0[13578]|1[02])31)|((\d{2})02(0[1-9]|1\d|2[0-8]))|(([13579][26]|[2468][048]|0[048])0229))\d{2}))(\d|X|x)$/,
        email: /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
        bankcard: /^[0-9]\d{1,29}$/,
        money: /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/,
        creditCode: /^(([0-9A-Za-z]{15})|([0-9A-Za-z]{18})|([0-9A-Za-z]{20}))$/,
        code: /^[0-9A-HJ-NPQRTUWXY]{8}-[0-9A-HJ-NPQRTUWXY]$/,
        business: /^\d{15}$/,
        company: /^[\u4e00-\u9fa5()（）\da-zA-Z&]{2,50}$/gi
    };
    return formReg[type];
};

/**
 * 生成UUID字符串
 * @returns
 */
export const getUuid = () => {
    var s = [];
    var hexDigits = '0123456789abcdef';
    for (var i = 0; i < 8; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = '4';
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
    s[8] = s[13] = s[18] = s[23];
    var uuid = s.join('');
    return uuid;
};

/**
 * 商城装修H5页面预览
 * @param {*} id
 * @param {*} status
 * @returns
 */
export const getPreviewUrl = (id, status, shopname, phonePrview) => {
    return `${
        import.meta.env.VITE_SHOP_H5_HOST
    }/#/pages/previewpage/index?pageId=${id}&publishStatus=${status}&createTime=${Date.now()}&shopName=${shopname}&phonePrview=${phonePrview}&ZW-PLATFORM-TOKEN=${storage.get(
        'ZW-TOKEN'
    )}&ZW-TENANT-ID=${storage.get('ZW-TENANT-ID')}&ZW-SHOP-ID=${storage.get('ZW-SHOPID')}`;
};
/**
 * 商城装修H5页面预览
 * @param {string} pageurl 页面路径，格式为 `/pages/previewpage/index`
 * @param {Object} params 页面参数
 */
export const getPreviewUrlNew = (pageurl, params) => {
    const query = {
        'ZW-TENANT-ID': storage.get('ZW-TENANT-ID'),
        'ZW-PLATFORM-TOKEN': storage.get('ZW-TOKEN'),
        'ZW-SHOP-ID': storage.get('ZW-SHOPID'),
        ...params
    };
    const queryString = Object.entries(query)
        .filter(([key, value]) => value !== undefined)
        .map(([key, value]) => `${key}=${value}`)
        .join('&');
    return `${import.meta.env.VITE_SHOP_H5_HOST}/#${pageurl}${queryString ? `?${queryString}` : ''}`;
};
/**
 * 导出文档
 * @res 数据
 * @name 文件名称
 * @fileType 自定义后缀
 */
export const exportExc = (res, name, fileType) => {
    const blob = new Blob([res.data], {
        type: 'application/msexcel'
    });
    const url = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    let fileName = fileType ? name + '.' + fileType : name + '.xlsx';
    if (res.headers && res.headers['content-disposition']) {
        const contentDisposition = res.headers['content-disposition'] || '';
        const fileNameMatch = contentDisposition.split('filename=');
        if (fileNameMatch.length === 2) {
            const temp = fileNameMatch[1].replace(new RegExp('\\+', 'gm'), '%20');
            fileName = decodeURIComponent(temp);
        }
    }
    link.setAttribute('download', fileName);
    document.body.appendChild(link);
    link.click();
    link.remove();
    window.URL.revokeObjectURL(url);
};

/**
 * 下载文件
 * @url 文件路径
 */
export const downLoadUrl = url => {
    const link = document.createElement('a');
    link.href = url;
    document.body.appendChild(link);
    link.click();
    link.remove();
};

/**
 * 是否有权限code
 * @code
 */
export const checkPermission = code => {
    const useAuth = store.useAuthStore();
    const permissions = useAuth.permissions;
    return permissions.has(code);
};

/**
 * 获取input元素以及光标位置信息对象
 * @param {*} elInput  input元素
 * @returns input元素以及光标位置信息对象
 */
export const cursorPosition = elInput => {
    const startPos = elInput.selectionStart;
    const endPos = elInput.selectionEnd;
    if (startPos === undefined || endPos === undefined) return;
    return {
        elInput,
        position: {
            startPos,
            endPos
        }
    };
};

/**
 * 根据input光标位置插入文字
 * @param {*} { elInput, position }  input元素以及光标位置信息对象
 * @param {*} insertTxt 要插入的文案
 * @param {*} currentObj 当前引入对象
 * @returns
 */
export const insertInputTxt = ({ elInput, position }, insertTxt, currentObj) => {
    const inputValue = elInput.value;
    const result = inputValue.substring(0, position.startPos) + insertTxt + inputValue.substring(position.endPos);
    elInput.value = result;
    elInput.focus();
    elInput.selectionStart = position.startPos + insertTxt.length;
    elInput.selectionEnd = position.endPos + insertTxt.length;
    return result;
    // document.click();
};

/**
 * 将整数装换为汉字
 * @param {*} num
 */
export const numberChinese = str => {
    if (!str) {
        return '';
    }
    let num = parseFloat(str);
    let strOutput = '',
        strUnit = '千百十亿千百十万千百十 ';
    num += '';
    strUnit = strUnit.substr(strUnit.length - num.length);
    for (var i = 0; i < num.length; i++) {
        strOutput += '零一二三四五六七八九'.substr(num.substr(i, 1), 1) + strUnit.substr(i, 1);
    }
    return strOutput
        .replace(/零[千百十]/g, '零')
        .replace(/零{2,}/g, '零')
        .replace(' ', '');
};

/**
 * 执行下载
 * @param {*} imgUrl
 * @param {*} name
 */
export const downloadImgHandler = (imgUrl, name) => {
    const a = document.createElement('a');
    const url = `${imgUrl}?v=${Date.now()}`;
    fetch(url)
        .then(res => res.blob())
        .then(blob => {
            a.href = URL.createObjectURL(blob);
            a.download = name;
            document.body.appendChild(a);
            a.click();
            window.URL.revokeObjectURL(a.href);
            document.body.removeChild(a);
        });
};

/**
 * 代理请求
 * @param {*} params 请求参数
 * @param {*} apiName 请求接口名称
 */
export const poxyRequest = (params, apiName = 'getPoxyServe') => {
    return new Promise((resolve, reject) => {
        request(apiName, params)
            .then(res1 => {
                if (res1.code === 0) {
                    resolve(res1);
                } else if ([10401, 10402, 10403].includes(res1.code)) {
                    //重新获取token
                    request('getLbsToken')
                        .then(tokenRes => {
                            if (tokenRes.code === 0) {
                                const { token, client_id, client_secret } = tokenRes.data;
                                storage.set('ZW-LBS-TOKEN', token);
                                storage.set('ZW-LBS-CLIENT-ID', client_id);
                                storage.set('ZW-LBS-CLIENT-SECRET', client_secret);
                                request(apiName, params).then(res2 => {
                                    resolve(res2);
                                });
                            } else {
                                reject(tokenRes);
                            }
                        })
                        .catch(err => {
                            reject(err);
                        });
                }
            })
            .catch(error => {
                reject(error);
            });
    });
};

// 获取id数组
export const getIdsArr = (arr, key = 'id') => {
    return arr.map(item => item[key]);
};
/**
 * 根据订单状态返回订单状态名称
 * @param {string, Number} orderStatus  订单状态值
 * @param {string, Number} type  类型：0-快递发货订单，1-自提订单
 * @param {Boolean} showSubLabel  特殊类型：分销订单
 */
export const translateOrderCode = (orderStatus, type = 0, showSubLabel = false) => {
    return (
        ((constants.ORDER_STATUS_NAME_OPTIONS[Number(showSubLabel) || 0] || [])[Number(orderStatus)] || {})[
            Number(type) || 0
        ] || ''
    );
};

// 不支持输入中文和空格 和中文符号
// //匹配这些中文标点符号 。 ？ ！ ， 、 ； ： “ ” ‘ ' （ ） 《 》 〈 〉 【 】 『 』 「 」 ﹃ ﹄ 〔 〕 … — ～ ﹏ ￥
export const inputFormatter = v => {
    const reg =
        /[\u3002|\uff1f|\uff01|\uff0c|\u3001|\uff1b|\uff1a|\u201c|\u201d|\u2018|\u2019|\uff08|\uff09|\u300a|\u300b|\u3008|\u3009|\u3010|\u3011|\u300e|\u300f|\u300c|\u300d|\ufe43|\ufe44|\u3014|\u3015|\u2026|\u2014|\uff5e|\ufe4f|\uffe5]/;
    return v.replace(/[(\u4e00-\u9fa5)(\s)]/g, '').replace(reg, '');
};

/**
 * 节流
 * @param   {function}  func        传入函数
 * @param   {number}    wait        表示时间窗口的间隔
 *
 * @return function
 */
export const throttle = (func, wait = 3000) => {
    return throttleFunc(func, wait, {
        leading: true,
        trailing: false
    });
};

/**
 * 驼峰转下划线
 * @param   {string}    str
 * @return string
 */
export const humpToUnderline = str => str.replace(/([A-Z])/g, '_$1').toLowerCase();

/* props 传参数 子组件数据双向绑定 */
export const useVModel = (props, emit, propName) => {
    return computed({
        get() {
            return new Proxy(props[propName], {
                set(obj, name, val) {
                    emit(`update:${propName}`, {
                        ...obj,
                        [name]: val
                    });
                    return true;
                }
            });
        },
        set(val) {
            emit(`update:${propName}`, val);
        }
    });
};

/**
 * 获取文案长度
 * @param text 文案
 * @param font 文字类型
 */
export const getTextWidth = (text, font = '14px sans-serif') => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.font = font;
    const metrics = context.measureText(text || '');
    return metrics.width;
};
